"""
扫描本机内网同一网段的ip
"""

import sys
import os
import threading
import datetime
import platform
from bdtime import tt
from queue import Queue
from tqdm import tqdm
from bdtime import show_ls
import subprocess
import socket


def get_host_ip():
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    finally:
        s.close()

    return ip


q = Queue()
output_file_path = '../ip_ls.txt'


def get_os():
    ret = platform.system()
    if ret == "Windows":
        return "n"
    else:
        return "c"


def ping_ip(ip_str):
    # ip_str = '192.168.0.1'
    cmd = f"ping -{get_os()} 1 {ip_str}"
    output = os.popen(cmd).readlines()

    flag = False
    for line in list(output):
        if not line:
            continue
        if str(line).upper().find("TTL") >= 0:
            flag = True
            break
    if flag:
        print("\n*** *** *** ip: %s is OK  *** *** ***\n" % ip_str)
        q.put(ip_str)


def ping_port_ls(ip_prefix, port_ls, interval=0.01, tqdm_i=None):
    for port_i in port_ls:
        ip = ('%s.%s' % (ip_prefix, port_i))
        ping_ip(ip)
        if tqdm_i:
            tqdm_i.desc = f'已扫描到{q.qsize()}个ip...扫描ip中...'
            tqdm_i.update(1)
        tt.sleep(interval)


def cut_ls(ls_1, times, ret_index=False, dbg=False):
    """
    将指定的一维列表`ls_1`切分`times`次

    - ret_index: 是则返回索引, 否则返回切分后的二维list
    """
    from math import ceil

    assert isinstance(ls_1, (list, tuple, range)), '仅支持(list, tuple)类型!'
    ls_1 = list(ls_1) if not isinstance(ls_1, list) else ls_1

    total = len(ls_1)
    batch = ceil(total / times)
    index_ls = [0]
    for i in range(times):
        index_i = index_ls[i] + batch
        index_i = index_i if index_i <= total + 1 else total
        index_ls.append(index_i)
    if dbg:
        print(f'index_ls: {index_ls}')
        print(f'workers: {times}, total: {total}, batch: {batch} --- total / workers: {round(total / times, 2)}')

    if ret_index:
        return index_ls
    else:
        ls_2 = []
        for i in range(times):
            if dbg:
                print(ls_1[index_ls[i]: index_ls[i + 1]])
            ls_2.append(ls_1[index_ls[i]: index_ls[i + 1]])
        return ls_2


def find_ip(ip_prefix, workers=128, port_ls=None):
    port_ls = list(range(0, 256)) if port_ls is None else port_ls
    tqdm_i = tqdm(total=len(port_ls))
    tqdm_i.desc = f'已扫描到{q.qsize()}个ip...扫描ip中...'

    ls_2 = cut_ls(port_ls, workers)
    task_ls = []
    interval = 0.1

    for port_ls in ls_2:
        task_i = threading.Thread(
            target=ping_port_ls,
            args=(ip_prefix, port_ls, interval, tqdm_i)
        )
        task_ls.append(task_i)
        task_i.start()
        tt.sleep(interval)

    while tqdm_i.n < tqdm_i.total:
        tt.sleep(0.1)

    ret = []
    while not q.empty():
        ret.append(q.get())
    ret = sorted(ret)

    return ret


if __name__ == "__main__":
    print(f'--- start_time: {tt.get_current_beijing_time_str(decimal_places=0)}')

    # assert len(sys.argv) >= 2, '请指定局域网网段net[192.168.xxx]!'
    # net = sys.argv[1]

    host_ip = get_host_ip()
    net = host_ip.split('.')[2]     # 本机网段
    print(f'*** 您的本机局域网IP为: {host_ip}, 将对[{net}]网段进行扫描!')
    tt.sleep(1)
    args = "".join(("192.168." + str(net) + ".1"))
    ip_prefix = '.'.join(args.split('.')[:-1])
    ip_ls = find_ip(ip_prefix)

    print(f'\n----------- 一共扫描到[{len(ip_ls)}]个内网IP! ---------')
    show_ls(ip_ls)
    print('-------------------------\n')

    if len(ip_ls) and output_file_path:
        s = '\n'.join(ip_ls)
        with open(output_file_path, 'w+', encoding='utf-8') as f:
            f.write(s)

    print(f'--- end_time: {tt.get_current_beijing_time_str(decimal_places=0)}')
    print(f'--- total cost time: {tt.now()}')

